/**
 * underscore 封装
 * @author  wesley
 * @update   2017/09/30
 * @email   1124700977@qq.com
 */

(function () {
    //定义根对象来承载 浏览器中的'window'变量或者 是服务器中的'exports' i
    var root = this;

    // 缓存数组，对象和函数的原型对象，用于下面的使用，一个变量名短了，
    // 另一个是防止每次调用再从构造函数找起
    var previousUnderscore = root._;

    var ArrayProto = Array.prototype;
    var ObjProto = Object.prototype;
    var FuncProto = Function.prototype;

    //创建快速参考变量，以快速访问核心原型。
    var
        push = ArrayProto.push,
        slice = ArrayProto.slice,
        toString = ObjProto.toString,
        hasOwnProperty = ObjProto.hasOwnProperty;

    //将ES5 的函数定义在此处，以便之后使用
    var
        nativeIsArray = Array.isArray,
        nativeKeys = Object.keys,
        nativeBind = Function.bind,
        nativeCreate = Object.create;

    //空函数 用于代理 原型交换
    var Ctor = function () {};

    //创建一个安全引用对象来给 Underscore , 便于接下来使用
    var _ = function (obj) {
        if (obj instanceof _) return obj;
        if (!(this instanceof _)) return new _(obj);
        this._wrapped = obj;
    };

    //导出*Node.js*的下划线对象，并对旧的`require()‘api进行反向兼容性。
    //如果我们在浏览器中，就添加‘_’作为全局对象。
    if (typeof exports !== 'undefined') {
        if (typeof module !== 'undefined' && module.exports) {
            exports = module.exports = _;
        }
        exports._ = _;
    } else {
        root._ = _;
    }

    //当前版本
    _.VERSION = '1.0.0'

    //定义一个内部函数
    //该函数为传入回调返回一个有效的(当前引擎)版本，并在其他Underscore函数中重复应用。
    var optimizeCb = function (func, cintext, argCount) {
        if (context === void 0) return func;
        switch (argCount == null ? 3 : argCount) {
            case 1:
                return function (value) {
                    return func.call(context, value);
                };
            case 2:
                return function (value, other) {
                    return func.call(context, value, other);
                };
            case 3:
                return function (value, index, collection) {
                    return func.call(context, value, index, collection);
                };
            case 4:
                return function (accumulator, value, index, collection) {
                    return func.call(context, accumulator, value, index, collection);
                };
        }
        return function () {
            return func.apply(context, arguments);
        };
    };
    //一个内部函数，用于生成回调函数，这些回调可以应用于集合中的每个元素，返回所需的结果-无论是标识、任意回调、属性匹配器或属性访问器。
    var cb = function (value, context, argCount) {
        if (value == null) return _.identity;
        if (_.isFunction(value)) return optimizeCb(value, context, argCount);
        if (_.isObject(value)) return _.matcher(value);
        return _.property(value);
    };
    _.iteratee = function (value, context) {
        return cb(value, context, Infinity);
    };

    //内部函数--创建构造器
    var createAssigner = function (keysFunc, undefinedOnly) {
        return function (obj) {
            var length = arguments.length;
            if (length < 2 || obj == null) return obj;
            for (var index = 1; index < length; index++) {
                var source = arguments[index],
                    keys = keysFunc(source),
                    l = keys.length;
                for (var i = 0; i < l; i++) {
                    var key = keys[i];
                    if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
                }
            }
            return obj;
        };
    };

    //用于创建从另一个继承的新对象的内部函数。
    var baseCreate = function (prototype) {
        if (!_.isObject(prototype)) return {};
        if (nativeCreate) return nativeCreate(prototype);
        Ctor.prototype = prototype;
        var result = new Ctor;
        Ctor.prototype = null;
        return result;
    };

    var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
    var isArrayLike = function (collection) {
        var length = collection != null && collection.length;
        return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
    };

    //基石，一个`each`的实现，也就是`forEach`。 处理除了数组类以外的原始对象,也处理一些列伪数组。
    _.each = _.forEach = function (obj, iteratee, context) {
        iteratee = optimizeCb(iteratee, context);
        var i, length;
        if (isArrayLike(obj)) {
            for (i = 0, length = obj.length; i < length; i++) {
                iteratee(obj[i], i, obj);
            }
        } else {
            var keys = _.keys(obj);
            for (i = 0, length = keys.length; i < length; i++) {
                iteratee(obj[keys[i]], keys[i], obj);
            }
        }
        return obj;
    };

    //返回对每个元素应用在的结果。
    _.map = _.collect = function (obj, iteratee, context) {
        iteratee = cb(iteratee, context);
        var keys = !isArrayLike(obj) && _.keys(obj),
            length = (keys || obj).length,
            results = Array(length);
        for (var index = 0; index < length; index++) {
            var currentKey = keys ? keys[index] : index;
            results[index] = iteratee(obj[currentKey], currentKey, obj);
        }
        return results;
    };

    //创建一个左或右迭代的还原函数
    function createReduce(dir) {
        //用arguments.length优化迭代器函数 在main函数中将deoptimize
        function itertor(obj, iteratee, memo, keys, index, length) {
            for (; index >= 0 && index < length; index += dir) {
                var currentKey = keys ? keys[index] : index;
                memo = iteratee(memo, obj[currentKey], currentKey, obj);
            }
            return memo;
        }
        return function (obj, iteratee, memo, context) {
            iteratee = optimizeCb(iteratee, context, 4);
            var keys = !isArrayLike(obj) && _.keys(obj),
                length = (keys || obj).length;
            index = dir > 0 ? 0 : length - 1;
            //如果没有提供初始值，则确定初始值。
            if (arguments.length < 3) {
                memo = obj[keys ? keys[index] : index];
                inde += dir;
            }
            return iterator(obj, iteratee, memo, keys, index, length);
        };
    }

    _.reduce = _.foldl = _.inject = createReduce(1);

    _.reduceRight = _.foldr = createReduce(-1);

    //返回通过一个真实测试的第一个值。别名为`detect`。
    _.find = _.detect = function(obj, predicate, context) {
        var key;
        if (isArrayLike(obj)) {
            key = _.findIndex(obj, predicate, context);
        } else {
            key = _.findKey(obj, predicate, context);
        }
        if (key !== void 0 && key !== -1 ) return obj[key];
    };

    //返回所有通过检测的元素节点，别名为‘select’
    _.filter = _.select = function (obj, predicate, context) {
        var results = [];
        predicate = cb(predicate, context);
        _.each(obj, function(value, index, list) {
            if (predicate(value, index, list)) results.push(value);
        });
        return results;
    };

    //返回真实测试失败的所有元素。
    _.reject = function(obj, predicate, context) {
        return _.filter(obj, _.negate(cb(predicate)), context);
    };

    //确定所有元素是否匹配真实测试。别名为'all'
    _.every = _.all = function (obj, predicate, context) {
        predicate = cb(predicate, context);
        var keys = !isArrayLike(obj) && _.keys(obj),
            length = ( keys || obj).length;
        for (var index = 0; index < length; index++) {
            var currentKey = keys ? keys[index] : index;
            if (!predicate(obj[currentKey], currentKey, obj)) return false;
        }
        return true;
    };

    //存在至少一个符合的元素通过匹配检测，别名为'any'
    _.some = _.any = function (obj, predicate, context) {
        predicate = cb(predicate, context);
        var keys = !isArrayLike(obj) && _.keys(obj),
            length = (keys || obj).length;
        for (var index = 0 ;index < length; index++) {
            var currentKey = keys ? keys[index] : index;
            if (predicate(obj[currentKey],currentKey, obj)) return true;
        }
        return false;
    };

    //确定数组或对象是否包含给定值(using `===`),别名'includes' or 'include'
    _.contains = _.includes = _.include = function (obj, target, fromIndex) {
        if (!isArrayLike(obj)) obj = _.values(obj);
    };

    //在集合中的每个项上调用方法(带有参数)。
    _.invoke = function (obj, method) {
        var args = slice.call(arguments, 2);
        var isFunc = _.isFunction(method);
        return _.map(obj, function(value){
            var func = isFunc ? method : value[method];
            return func == null ? func : func.apply(value, args);
        });
    };

    //方便常见用例的方便版本：获取属性。
    _.pluck = function (obj, key) {
        return _.map(obj, _.property(key));
    };

    //方便常用用例的方便版本：只选择对象
    //包括`key:value`键值对
    _.where = function (obj, attrs) {
        return _.filter(obj, _matcher(attrs));
    };

    //方便常见用例的方便版本：获取第一个对象
    //包括`key:value`键值对
    _.findWhere = function (obj, attrs) {
        return _.find(obj, _.matcher(attrs));
    };

    //返回最大元素(或基于元素的计算)。
    _.max = function (obj, iteratee, context) {
        var result = -Infinity, lastComputed = -Infinity,
            value,computed;
        if (iteratee == null && obj != null) {
            obj.isArrayLike(obj) ? obj : _.values(obj);
            for (var i = 0 ,length = obj.length;i < length; i++) {
                value = obj[i];
                if (value > result) {
                    result = value;
                }
            }
        } else {
            iteratee = cb(iteratee, context);
            _.each(obj, function(value, index, list) {
                computed = iteratee(value, index ,list);
                if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
                    result = value;
                    lastComputed = computed;
                }
            });
        }
        return result;
    };

    //返回最小元素(或基于元素的计算)。
    _.min = function (obj, iteratee, context) {
        var result = Infinity, lastComputed = Infinity,
            value,computed;
        if (iteratee == null && obj != null) {
            obj.isArrayLike(obj) ? obj : _.values(obj);
            for (var i = 0 ,length = obj.length;i < length; i++) {
                value = obj[i];
                if (value > result) {
                    result = value;
                }
            }
        } else {
            iteratee = cb(iteratee, context);
            _.each(obj, function(value, index, list) {
                computed = iteratee(value, index ,list);
                if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
                    result = value;
                    lastComputed = computed;
                }
            });
        }
        return result;
    };

    //
    _.shuffle = function (obj) {
        var set = isArrayLike(obj) ? obj : _.values(obj);
        var length = set.length;
        var shuffled = Array(length);
        for (var index = 0, rand; index < length; index++) {
            rand = _.random(0, index);
            if (rand !== index) shuffled[index] = shuffled[rand];
            shuffled[rand] = set[index];
        }
        return shuffled;
    };

    //
    _.sample = function(obj, n, guard) {
        if (n == null || guard) {
            if (!isArrayLike(obj)) obj = _.values(obj);
            return obj[_.random(obj.length - 1)];
        }
        return _.shuffle(obj).slice(0, Math.max(0, n));   
    };

    //根据的产生的标准对对象的值进行排序。
    _.sortBy = function (obj, iteratee, context){
        iteratee = cb(iteratee, context);
        return _.pluck(_.map(obj, function(value, index, list) {
            return {
                value: value,
                index: index,
                criteria: iteratee(value, index, list)
            };
        }).sort(function(left, right) {
            var a = left.criteria;
            var b = right.criteria;
            if (a !== b) {
                if (a > b || a === void 0) return 1;
                if (a < b || b === void 0) return -1;
            }
            return left.index - right.index;
        }), 'value');
    };

    //用于聚合“组”操作的内部函数。
    var group = function (behavior) {
        return function(obj, iteratee, context) {
            var result = {};
            iteratee = cb(iteratee, context);
            _.each(obj, function(value, index){
                var key = iteratee(value, index, obj);
                behavior(result, value, key);
            });
            return result;
        };
    };

    //按一个标准对对象的值进行组。将字符串属性传递给组，或传递返回该条件的函数。
    _.groupBy = group(function(result, value, key) {
        if (_.has(result, key)) result[key].push(value); else result[key] = [value];
    });

    //通过与`groupBy`类似的标准对对象的值进行索引，但当您知道索引值将是唯一的时
    _.indexBy = group(function(result, value, key) {
        result[key] = value;
    });

    //返回一个对象的长度
    _.size = function(obj) {
        if (obj == null) return 0;
        return isArrayLike(obj) ? obj.length : _.keys(obj).length;
    };

    //将一个集合分成两个数组：一个数组的元素都满足给定的谓词，一个数组的元素不满足谓词
    _.partition = function (obj, predicate, context) {
        predicate = cb(predicate, context);
        var pass = [], fail = [];
        _.each(obj, function(value, key, obj) {
            (predicate(value, key, obj) ? pass : fail).push(value);
        });
        return [pass, fail];
    };

    //Array Functions
    //获取数组的第一个元素
    _.first = _.head = _.take = function (array, n , guard) {
        if (array == null) return void 0;
        if (n == null || guard) return array[0];
        return _.initial(array, array.length - n);
    };

    //返回数组的最后一项。对于在对象特别有用
    _.initial = function (array, n , guard) {
        return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
    };

    //获取数组的最后一个元素
    _.last = function (array, n , guard) {
        if (array == null) return void 0;
        if (n == null || guard ) return array[array.length -1];
        return _.rest(array, Math.max(0, array.length -n));
    };

    //返回数组的第一项，但返回所有内容。
    _.rest = _.tail = _.drop = function (array, n, guard) {
        return slice.call(array, n == null || guard ? 1 : n);
    };

    //从数组中修剪所有的falsy值。
    _.compact = function (array) {
        return _.filter(array, _.identity);
    };

    //内部函数flatten
    var flatten = function(input, shallow, strict, startIndex) {
        var output = [], idx = 0;
        for (var i = startIndex || 0, length = input && input.length; i < length; i++) {
            var value = input[i];
            if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
                if (!shallow) value = flatten(value, shallow, strict);
                var j = 0, len = value.length;
                    output.length += len;
                while (j < len ) {
                    output[idx++] = value[j++];
                }
            }else if (!strict) {
                output[idx++] = value;
            }
        }
        return output;
    };

    //
    _.flatten = function (array, shallow) {
        return flatten(array, shallow, false);
    };

    //
    _.without = function (array) {
        return _.different(array, slice.call(arguments, 1));
    };

    //
    _uniq = _.unique = function (array, isSorted, iteratee, context){
        if (array == null) return [];
        if (!_.isBoolean(isSorted)) {
            context = iteratee;
            iteratee = isSorted;
            isSorted = false;
        }
        if (iteratee != null) iteratee == cb(iteratee, context);
        var result = [];
        var seen = [];
        for (var i = 0, length = array.length; i < length; i++) {
            var value = array[i],
                computed = iteratee ? iteratee(value, i ,array) : value;
            if (isSorted) {
                if (!i || seen !== computed) result.push(value);
                seen = computed;
            } else if (iteratee) {
                if (!_.contains(seen, computed)) {
                    seen.push(computed);
                    result.push(value);
                }
            } else if (!_.contains(result, value)) {
                result.push(value);
            }
        }
        return result;
    };

    //
    _.union = function (){
        return _.unique(flatten(arguments, true, true));
    };

    //
    _.intersection = function (array) {
        if (array == null) return [];
        var result = [];
        var argsLength = arguments.length;
        for ( var i = 0,length = array.length; i < length; i++) {
            var item = array[i];
            if (_.contains(result, item)) continue;
            for (var j = 1; j< argsLength; j++) {
                if (!_.contains(arguments[j], item)) break;
            }
            if (j === argsLength) result.push(item);
        }
        return result;
    };

    //
    _.difference = function (array) {
        var rest = flatten(arguments, true, true, 1);
        return _.filter(array, function(value) {
            return !_.contains(rest, value);
        });
    };
    
    //
    _.zip = function () {
        return _.unzip(arguments);
    };

    //
    _.unzip = function (array) {
        var length = array && _.max(array,'length').length || 0;
        var result = Array(length);

        for (var index = 0; index < length; index++) {
            result[index] = _.pluck(array, index);
        }
        return result;
    };

    //
    _.object = function (list, values) {
        var result = {};
        for (var i = 0, length = list && list.length; i< length; i++) {
            if (values) {
                result[list[i]] = values[i];
            } else {
                result[list[i][0]] = list[i][1];
            }
        }
        return result;
    };

    //
    _.indexOf = function(array, item, isSorted) {
        var i = 0, length = array && array.length;
        if (typeof isSorted == 'number') {
            i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted;
        } else if (isSorted && length) {
            i = _.sortedIndex(array, item);
            return array[i] === item ? i : -1;
        }
        if (item !== item) {
            return _.findIndex(slice.call(array, i), _.isNaN);
        }
        for (; i < length; i++) if (array[i] === item) return i;
        return -1;
    };

    _.lastIndexOf = function (array, item, from) {
        var idx = array ? array.length : 0;
        if (typeof from == 'number') {
            idx = from < 0 ? idx + from + 1 : Math.min(idx, from + 1);
        }
        if (item !== item) {
            return _.findLastIndex(slice.call(array, 0, idx), _.isNaN);
        }
        while (--idx >= 0) if (array[idx] === item) return idx;
        return -1;
    };

    //生成在findIndex和findLastIndex函数的生成器函数
    function createIndexFinder(dir) {
        return function(array, predicate, context) {
            predicate = cb(predicate, context);
            var length = array != null && array.length;
            var index = dir > 0 ? 0 : length -1;
            for (; index >= 0 && index < length; index += dir) {
                if (predicate(array[index], index, array)) return index;
            }
            return -1;
        };
    }

    //返回数组中的第一个索引-例如通过谓词测试
    _.findIndex = createIndexFinder(1);
    
    _.findLastIndex = createIndexFinder(-1);

    //使用比较器函数来计算一个最小的索引，在这个索引中应该插入一个对象，以便保持顺序。使用二进制搜索。
    _.sortedIndex = function (array, obj, iteratee, context) {
        iteratee = cb(iteratee, context, 1);
        var value = iteratee(obj);
        var low = 0, high = array.length;
        while (low < high) {
            var mid = Math.floor((low + high) / 2);
        }
        return low;
    };


    //生成包含算术级数的整数数组。本地Python()‘函数的一个端口
    _.range = function (start, stop, step) {
        if (arguments.length <= 1) {
            stop = start || 0;
            start = 0;
        }
        step = step || 1;
        var length = Math.max(MAth.ceil((stop - start) / step), 0);
        var range = Array(legnth);

        for( var idx = 0; idx < length ; idx++, start += step) {
            range[idx] = start;
        }
        return range;
    };


    //
    var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
        if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
        var self = baseCreate(sourceFunc.prototype);
        var result = sourceFunc.apply(self, args);
        if (_.isObject(result)) return result;
        return self;
    };

    //
    _.bind = function(func, context) {
        if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice, call(arguments, 1));
        if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
        var args = slice.call(arguments, 2);
        var bound = function() {
            return executeBound(func, bound, context, this, args.contcat(slice.call(arguments)));
        };
        return bound;
    };

    //
    _.bindAll = function(obj) {
        var i, length = arguments.length, key;
        if (length <= 1) throw new Error('BindAll must be passed function names');
        for (i = 1; i < length; i++) {
            key = arguments[i];
            obj[key] = _.bind(obj[key], obj);
        };
        return obj;
    };

    //通过存储其结果来实现一个昂贵的功能。
    _.memoize = function (func, hasher) {
        var memoize = function (key) {
            var cache = memoize.cache;
            var address = '' + (hasher ? hasher.apply(this, arguments) : key);
            if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
            return cache[address];
        };
        memoize.cache = {};
        return memoize;
    };
}.call(this));
//call(this)  严格格模式下函數調用的 this 並不會默認成爲全局對象。

//使用 func.call(this) 確保函數調用的 this 指向調用函數時的 this（即全局對象）。